package com.wang.tool.monitor.weaver;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wang.tool.monitor.LinkLogUtil;
import com.wang.tool.monitor.aop.AnnotationWeaver;
import com.wang.tool.monitor.core.MonitorInfo;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description 基础编织器
 * 恒输出 出错 超时 debug 非预期状态码  输出入参details
 * 恒输出 超时 debug 非预期状态码 输出出参details
 * @Author shaopeng
 * @Date 2022/10/26
 * @Version 1.0
 */
public class BaseAnnotationWeaver<R> implements AnnotationWeaver<R> {

    /**
     * 切面配置是否恒输出入参
     * 如果切面配置了,自己就不需要输出了
     */
    protected boolean advicePrintParam;

    /**
     * 切面配置是否恒输出出参
     * 如果切面配置了,自己就不需要输出了
     */
    protected boolean advicePrintResult;

    /**
     * 期望状态码 可为空
     * expectStatus
     * 结果码和预期不一致会在details中输出入参
     */
    protected String expectStatus;


    /**
     * 特殊情况在details中输出结果
     * 超时 非预期status debug
     * specialDetailResult
     */
    protected boolean specialPrintResult;

    /**
     * 参数详情下标
     * 不填代表所有
     * 0开始
     * 可被子类重写
     *
     * @see BaseAnnotationWeaver#fetchParam
     */
    protected int[] paramIndexs;

    @Override
    public void init(String group, String biz, String action, boolean printParam, boolean printResult, Map<String, String> cfg) {
        JSONObject config = new JSONObject();
        config.putAll(cfg);
        this.advicePrintParam = printParam;
        this.advicePrintResult = printResult;
        String expectStatus = config.getString("expectStatus");
        if (expectStatus != null && !expectStatus.isEmpty()) {
            this.expectStatus = expectStatus;
        }
        this.specialPrintResult = config.getBooleanValue("specialPrintResult");

        String pisStr = config.getString("paramIndexs");
        if (pisStr == null || pisStr.isEmpty()) {
            return;
        }
        List<Integer> pisList = Arrays.stream(pisStr.split(",")).map(Integer::parseInt).sorted().distinct().collect(Collectors.toList());
        if (pisList.isEmpty()) {
            return;
        }
        this.paramIndexs = new int[]{pisList.size()};
        for (int i = 0; i < pisList.size(); i++) {
            int pi = pisList.get(i);
            if (pi < 0) {
                throw new IllegalArgumentException("pi must larger -1");
            }
            this.paramIndexs[i] = pi;
        }
    }

    @Override
    public void weaveOnBefore(MonitorInfo<R> monitorInfo) throws Throwable {
    }

    @Override
    public final void weaveOnAfter(MonitorInfo<R> monitorInfo) throws Throwable {
        internalAfter(monitorInfo);
        boolean notExpectStatus = expectStatus != null && !expectStatus.equals(monitorInfo.getStatus());
        if (notExpectStatus) {
            LinkLogUtil.newTagLog(MonitorInfo.TAG_NOT_EXPECT_STATUS)
                    .v1(expectStatus)
                    .v2(monitorInfo.getStatus())
                    .pointLink(monitorInfo.getLink())
                    .log();
        }
        //恒输出/错/隐性异常/慢/debug/非预期状态
        boolean special = monitorInfo.isFailOut() || monitorInfo.isImplicitError() || notExpectStatus || monitorInfo.isDebug();
        if (!advicePrintParam && (special || monitorInfo.isSlow())) {
            Object param = fetchParam(monitorInfo.getParams());
            if (param != null) {
                //载荷中记录入参
                monitorInfo.addDetailParam(param);
//                monitorInfo.addShortDetailOnDebug(MonitorInfo.PARAM, param);
            }
        }

        //载荷中记录结果
        if (!advicePrintResult && (specialPrintResult && special)) {
            R fetchResult = fetchResult(monitorInfo.getResult());
            if (fetchResult != null) {
                monitorInfo.addDetailResult(fetchResult);
                //链路上不记录结果,太大了
//                monitorInfo.addShortDetailOnDebug(MonitorInfo.RESULT, "结果详情请查看日志");
            }
        }
    }


    /**
     * 更新状态等
     *
     * @param monitorInfo
     */
    protected void internalAfter(MonitorInfo<R> monitorInfo) throws Throwable {
    }


    @Override
    public Object fetchParam(Object[] params) {
        if (params == null || params.length == 0) {
            return null;
        }
        if (paramIndexs == null) {
            return params.length == 1 ? params[0] : params;
        }
        Object[] nps = new Object[paramIndexs.length];
        for (int i = 0; i < paramIndexs.length; i++) {
            int pi = paramIndexs[i];
            if (params.length <= pi) {
                break;
            }
            nps[i] = params[pi];
        }
        return nps.length == 1 ? nps[0] : nps;
    }

    @Override
    public R fetchResult(R result) {
        return result;
    }


    public void setAdvicePrintParam(boolean advicePrintParam) {
        this.advicePrintParam = advicePrintParam;
    }

    public void setAdvicePrintResult(boolean advicePrintResult) {
        this.advicePrintResult = advicePrintResult;
    }

    public void setExpectStatus(String expectStatus) {
        this.expectStatus = expectStatus;
    }


    public void setSpecialPrintResult(boolean specialPrintResult) {
        this.specialPrintResult = specialPrintResult;
    }

    public void setParamIndexs(int[] paramIndexs) {
        this.paramIndexs = paramIndexs;
    }

    /**
     * 不要通过重写此函数达到配置目的
     * 实际逻辑直接使用属性
     *
     * @return
     */
    public final boolean isAdvicePrintParam() {
        return advicePrintParam;
    }

    /**
     * 不要通过重写此函数达到配置目的
     * 实际逻辑直接使用属性
     *
     * @return
     */
    public final boolean isAdvicePrintResult() {
        return advicePrintResult;
    }

    /**
     * 不要通过重写此函数达到配置目的
     * 实际逻辑直接使用属性
     *
     * @return
     */
    public final String getExpectStatus() {
        return expectStatus;
    }

    /**
     * 不要通过重写此函数达到配置目的
     * 实际逻辑直接使用属性
     *
     * @return
     */
    public final boolean isSpecialPrintResult() {
        return specialPrintResult;
    }

    /**
     * 不要通过重写此函数达到配置目的
     * 实际逻辑直接使用属性
     *
     * @return
     */
    public final int[] getParamIndexs() {
        return paramIndexs;
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
}
